/*
 * kernallocks.cc
 *
 *  Created on: Jun 10, 2011
 *      Author: peckb1
 */

#include "kernellocks.h"
#include "thread.h"
#include "system.h"
// --------------------------------------------------
// Kernel Locks
// --------------------------------------------------

KernelLocks::KernelLocks() {
    _currentNewLockId = 0;
    _kernelLockIndexToReUse = new List();
    _kernelLockTableLock = new Lock("Kernel Lock Map Lock");

    _currentNewConditionId = 0;
    _kernelConditionIndexToReUse = new List();
    _kernelConditionTableLock = new Lock("Kernel Condition Map Lock");
}

KernelLocks::~KernelLocks() {
    delete _kernelLockIndexToReUse;
    delete _kernelLockTableLock;
}

bool
KernelLocks::ValidLockId(int lockId, char* task) {
    if(lockId < 0 || lockId > (((int)_kernelLockMap.size()) - 1)) {
        printf("[KERNEL ERROR] Unable to %s Lock, Id did not exist.\n", task);
        return FALSE;
    }

    return TRUE;
}

bool
KernelLocks::ValidLockUserSpace(int lockId, AddrSpace* addrSpace, char* task) {
    KernelLock* kernelLock = _kernelLockMap[lockId];

    // make sure we are at least in the right user program
    if(kernelLock->CurrentUserSpace() != addrSpace) {
        printf("[KERNEL PID#%i, TID#%i ERROR] A user program who didn't have a lock in its address space tried to %s a lock.\n",currentThread->getProcessID(), currentThread->getThreadID(), task);
        return FALSE;
    }

    return TRUE;
}

bool
KernelLocks::LockDeletionNotCalled(int lockId, char* task) {
    KernelLock* kernelLock = _kernelLockMap[lockId];

    // first check if the lock is to be deleted
    if(kernelLock->IsToBeDeleted()) {
        printf("[KERNEL PID#%i, TID#%i ERROR] Unable to %s Lock %i, it has been scheduled for deletion.\n",currentThread->getProcessID(), currentThread->getThreadID(), task, lockId);
        return FALSE;
    }
    return TRUE;
}

bool
KernelLocks::LockActive(int lockId, char* task) {
    KernelLock* kernelLock = _kernelLockMap[lockId];

    // check that the lock is active
    if(!kernelLock->IsActive()) {
        printf("[KERNEL PID#%i, TID#%i ERROR] Unable to %s Lock %i, it has been deleted.\n",currentThread->getProcessID(), currentThread->getThreadID(), task, lockId);
        return FALSE;
    }

    return TRUE;
}

bool
KernelLocks::ProperOwner(int lockId, char* task) {
    KernelLock* kernelLock = _kernelLockMap[lockId];

    if(kernelLock->GetLock()->isHeldByCurrentThread()) {
        return TRUE;
    } else {
        printf("Unable to %s on Lock %i, not current owner.\n", task, lockId);
        return FALSE;
    }
}


// Methods to interact with Kernel Locks
int
KernelLocks::CreateLock(char* name, AddrSpace* addrSpace) {
    // get the lock on altering the data
    _kernelLockTableLock->Acquire();

    int idOfNewLock = _currentNewLockId;
    _currentNewLockId++;
    KernelLock* newLock = new KernelLock(name, idOfNewLock, addrSpace);
    _kernelLockMap.insert(std::pair<int, KernelLock*>(idOfNewLock, newLock));

    // release the lock on altering the data
    _kernelLockTableLock->Release();

    return idOfNewLock;
}

void
KernelLocks::Acquire(int lockId, AddrSpace* addrSpace) {
    // get the lock on altering the data
    _kernelLockTableLock->Acquire();

    char* taskName = "Acquire";

    bool isValid =  ValidLockId(lockId, taskName) &&
                    ValidLockUserSpace(lockId, addrSpace, taskName) &&
                    LockActive(lockId, taskName) &&
                    LockDeletionNotCalled(lockId, taskName);

    if(isValid) {
        KernelLock* kernelLock = _kernelLockMap[lockId];

        // if we get here then we should be able to actually acquire the lock
        kernelLock->IncrementWaitingThreads();

        // release the lock on altering the data
        _kernelLockTableLock->Release();

        // call acquire on the lock itself
        kernelLock->GetLock()->Acquire();
    } else {
        // release the lock on altering the data
        _kernelLockTableLock->Release();
    }
}

void
KernelLocks::Release(int lockId, AddrSpace* addrSpace) {
    // get the lock on altering the data
    _kernelLockTableLock->Acquire();

    char* taskName = "Release";

    bool isValid =  ValidLockId(lockId, taskName) &&
                    ValidLockUserSpace(lockId, addrSpace, taskName) &&
                    LockActive(lockId, taskName) &&
                    ProperOwner(lockId, taskName);

    if(isValid) {
        KernelLock* kernelLock = _kernelLockMap[lockId];

        // if we get here then we should be able to actually acquire the lock
        kernelLock->DecrementWaitingThreads();

        // release the lock on altering the data
        _kernelLockTableLock->Release();

        // call acquire on the lock itself
        kernelLock->GetLock()->Release();
    } else {
        // release the lock on altering the data
        _kernelLockTableLock->Release();
    }
}

void
KernelLocks::DestroyLock(int lockId, AddrSpace* addrSpace) {
    // get the lock on altering the data
    _kernelLockTableLock->Acquire();

    char* taskName = "Destroy";

    bool isValid =  ValidLockId(lockId, taskName) &&
                    ValidLockUserSpace(lockId, addrSpace, taskName) &&
                    LockActive(lockId, taskName) &&
                    LockDeletionNotCalled(lockId, taskName);

    if(isValid) {
        KernelLock* kernelLock = _kernelLockMap[lockId];

        if(kernelLock->ThreadsWaiting() > 0) {
            kernelLock->DeleteWhenReady();
        } else {
            kernelLock->DeleteLock();
        }

        _kernelLockTableLock->Release();
    } else {
        // release the lock on altering the data
        _kernelLockTableLock->Release();
    }
}

bool
KernelLocks::ValidConditionId(int cvId, char* task) {
    if(cvId < 0 || cvId > (((int)_kernelConditionMap.size()) - 1)) {
        printf("[KERNEL ERROR] Unable to %s Lock, Id did not exist.\n", task);
        return FALSE;
    }

    return TRUE;
}

bool
KernelLocks::ValidConditionUserSpace(int cvId, AddrSpace* addrSpace, char* task) {
    KernelCondition* kernelCondition = _kernelConditionMap[cvId];

    // make sure we are at least in the right user program
    if(kernelCondition->CurrentUserSpace() != addrSpace) {
        printf("[KERNEL PID#%i, TID#%i ERROR] A user program who didn't have a Condition in its address space tried to %s.\n", currentThread->getProcessID(), currentThread->getThreadID(),task);
        return FALSE;
    }

    return TRUE;
}

bool
KernelLocks::ConditionDeletionNotCalled(int cvId, char* task) {
    KernelCondition* kernelCondition = _kernelConditionMap[cvId];

    // first check if the lock is to be deleted
    if(kernelCondition->IsToBeDeleted()) {
        printf("[KERNEL PID#%i, TID#%i ERROR] Unable to %s on Condition %i, it has been scheduled for deletion.\n", currentThread->getProcessID(), currentThread->getThreadID(),task, cvId);
        return FALSE;
    }
    return TRUE;
}

bool
KernelLocks::ConditionActive(int cvId, char* task) {
    KernelCondition* kernelCondition = _kernelConditionMap[cvId];

    // check that the lock is active
    if(!kernelCondition->IsActive()) {
        printf("[KERNEL PID#%i, TID#%i ERROR] Unable to %s Condition %i, it has been deleted.\n",currentThread->getProcessID(), currentThread->getThreadID(), task, cvId);
        return FALSE;
    }

    return TRUE;
}

// Methods to interact with Kernel CVs
int
KernelLocks::CreateCV(char* name, AddrSpace* addrSpace) {
    // get the lock on altering the data
    _kernelConditionTableLock->Acquire();

    int idOfNewCondition = _currentNewConditionId;
    _currentNewConditionId++;

    KernelCondition* newCondition = new KernelCondition(name, idOfNewCondition, addrSpace);
    _kernelConditionMap.insert(std::pair<int, KernelCondition*>(idOfNewCondition, newCondition));

    // release the lock on altering the data
    _kernelConditionTableLock->Release();

    return idOfNewCondition;
}

void
KernelLocks::Wait(int cvId, int lockId, AddrSpace* addrSpace) {
    _kernelConditionTableLock->Acquire();
    _kernelLockTableLock->Acquire();

    char* taskName = "Wait";

    bool isValid =  ValidConditionId(cvId, taskName) &&
                    ValidConditionUserSpace(cvId, addrSpace, taskName) &&
                    ConditionActive(cvId, taskName) &&
                    ConditionDeletionNotCalled(cvId, taskName) &&
                    ValidLockId(lockId, taskName) &&
                    ValidLockUserSpace(lockId, addrSpace, taskName) &&
                    LockActive(lockId, taskName) &&
                    LockDeletionNotCalled(lockId, taskName);

    if(isValid) {
        KernelCondition* kernelCondition = _kernelConditionMap[cvId];
        KernelLock* kernelLock = _kernelLockMap[lockId];

        kernelCondition->IncrementWaitingThreads();

        _kernelLockTableLock->Release();
        _kernelConditionTableLock->Release();

        kernelCondition->GetCondition()->Wait(kernelLock->GetLock());
    } else {
        _kernelLockTableLock->Release();
        _kernelConditionTableLock->Release();
    }
}

void
KernelLocks::Signal(int cvId, int lockId, AddrSpace* addrSpace) {
    _kernelConditionTableLock->Acquire();
    _kernelLockTableLock->Acquire();

    char* taskName = "Signal";

    bool isValid =  ValidConditionId(cvId, taskName) &&
                    ValidConditionUserSpace(cvId, addrSpace, taskName) &&
                    ConditionActive(cvId, taskName) &&
                    ValidLockId(lockId, taskName) &&
                    ValidLockUserSpace(lockId, addrSpace, taskName) &&
                    LockActive(lockId, taskName);

    if(isValid) {
        KernelCondition* kernelCondition = _kernelConditionMap[cvId];
        KernelLock* kernelLock = _kernelLockMap[lockId];

        kernelCondition->DecrementWaitingThreads();

        kernelCondition->GetCondition()->Signal(kernelLock->GetLock());

        _kernelLockTableLock->Release();
        _kernelConditionTableLock->Release();
    } else {
        _kernelLockTableLock->Release();
        _kernelConditionTableLock->Release();
    }
}

void
KernelLocks::Broadcast(int cvId, int lockId, AddrSpace* addrSpace) {
    _kernelConditionTableLock->Acquire();
    _kernelLockTableLock->Acquire();

    char* taskName = "Broadcast";

    bool isValid =  ValidConditionId(cvId, taskName) &&
                    ValidConditionUserSpace(cvId, addrSpace, taskName) &&
                    ConditionActive(cvId, taskName) &&
                    ValidLockId(lockId, taskName) &&
                    ValidLockUserSpace(lockId, addrSpace, taskName) &&
                    LockActive(lockId, taskName);

    if(isValid) {
        KernelCondition* kernelCondition = _kernelConditionMap[cvId];
        KernelLock* kernelLock = _kernelLockMap[lockId];

        kernelCondition->ClearWaitingThreads();

        kernelCondition->GetCondition()->Broadcast(kernelLock->GetLock());

        _kernelLockTableLock->Release();
        _kernelConditionTableLock->Release();
    } else {
        _kernelLockTableLock->Release();
        _kernelConditionTableLock->Release();
    }
}

void
KernelLocks::DestroyCV(int cvId, AddrSpace* addrSpace) {
    _kernelConditionTableLock->Acquire();

    char* taskName = "Destroy";

    bool isValid =  ValidConditionId(cvId, taskName) &&
                    ValidConditionUserSpace(cvId, addrSpace, taskName) &&
                    ConditionActive(cvId, taskName) &&
                    ConditionDeletionNotCalled(cvId, taskName);

    if(isValid) {
        KernelCondition* kernelCondition = _kernelConditionMap[cvId];

        if(kernelCondition->ThreadsWaiting() > 0) {
            kernelCondition->DeleteWhenReady();
        } else {
            kernelCondition->DeleteCondition();
        }
        _kernelConditionTableLock->Release();
    } else {
        _kernelConditionTableLock->Release();
    }
}

// --------------------------------------------------
// Kernel Lock
// --------------------------------------------------


KernelLock::KernelLock(char* name, int UID, AddrSpace* _addrSpace) {
    _myAddrSpace = _addrSpace;
    _lock = new Lock(name);
    _isDeleted = FALSE;
    _isToBeDeleted = FALSE;
    _id = UID;
    _threadsWaiting = 0;
}

KernelLock::~KernelLock() {
//    delete _myAddrSpace;
    delete _lock;
}

void
KernelLock::DeleteLock() {
    _isDeleted = TRUE;
}

void
KernelLock::DeleteWhenReady() {
    _isToBeDeleted = TRUE;
}

void
KernelLock::IncrementWaitingThreads() {
    _threadsWaiting ++;
}

void
KernelLock::DecrementWaitingThreads() {
    _threadsWaiting --;
    if(_threadsWaiting <= 0 && IsToBeDeleted()) {
        _isDeleted = TRUE;
    }
}

int
KernelLock::ThreadsWaiting() {
    return _threadsWaiting;
}

bool
KernelLock::IsActive() {
    return !_isDeleted;
}

bool
KernelLock::IsToBeDeleted() {
    return _isToBeDeleted;
}

AddrSpace*
KernelLock::CurrentUserSpace() {
    return _myAddrSpace;
}

Lock*
KernelLock::GetLock() {
    return _lock;
}

// --------------------------------------------------
// Kernel Condition
// --------------------------------------------------

KernelCondition::KernelCondition(char* name, int UID, AddrSpace* _addrSpace) {
    _myAddrSpace = _addrSpace;
    _condition = new Condition(name);
    _isDeleted = FALSE;
    _isToBeDeleted = FALSE;
    _id = UID;
    _threadsWaiting = 0;
}
KernelCondition::~KernelCondition() {
//    delete _myAddrSpace;
    delete _condition;
}

void
KernelCondition::DeleteCondition() {
    _isDeleted = TRUE;
}

void
KernelCondition::DeleteWhenReady() {
    _isToBeDeleted = TRUE;
}

void
KernelCondition::IncrementWaitingThreads() {
    _threadsWaiting ++;
}

void
KernelCondition::DecrementWaitingThreads() {
    _threadsWaiting --;
    if(_threadsWaiting <= 0 && IsToBeDeleted()) {
        _isDeleted = TRUE;
    }
}

void
KernelCondition::ClearWaitingThreads() {
    _threadsWaiting = 0;
}

int
KernelCondition::ThreadsWaiting() {
    return _threadsWaiting;
}

bool
KernelCondition::IsActive() {
    return !_isDeleted;
}

bool
KernelCondition::IsToBeDeleted() {
    return _isToBeDeleted;
}

AddrSpace*
KernelCondition::CurrentUserSpace() {
    return _myAddrSpace;
}

Condition*
KernelCondition::GetCondition() {
    return _condition;
}
